<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=635553
-->
<head>
  <title>Test for Bug 635553</title>
  <script type="application/javascript" src="/MochiKit/packed.js"></script>
  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=635499">Mozilla Bug 635499</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">

/** Test for Bug 635553 **/

var data = [
  { type: 'hidden', apply: false },
  { type: 'text', apply: false },
  { type: 'search', apply: false },
  { type: 'tel', apply: false },
  { type: 'url', apply: false },
  { type: 'email', apply: false },
  { type: 'password', apply: false },
  { type: 'date', apply: true },
  { type: 'month', apply: true },
  { type: 'week', apply: true },
  { type: 'time', apply: true },
  { type: 'datetime-local', apply: true },
  { type: 'number', apply: true },
  { type: 'range', apply: true },
  { type: 'color', apply: false },
  { type: 'checkbox', apply: false },
  { type: 'radio', apply: false },
  { type: 'file', apply: false },
  { type: 'submit', apply: false },
  { type: 'image', apply: false },
  { type: 'reset', apply: false },
  { type: 'button', apply: false },
];

var input = document.createElement("input");
document.getElementById('content').appendChild(input);

/**
 * @aValidity - boolean indicating whether the element is expected to be valid
 *   (aElement.validity.valid is true) or not. The value passed is ignored and
 *   overridden with true if aApply is false.
 * @aApply - boolean indicating whether the min/max attributes apply to this
 *   element type.
 * @aRangeApply - A boolean that's set to true if the current input type is a
 *   "[candidate] for constraint validation" and it "[has] range limitations"
 *   per http://www.whatwg.org/specs/web-apps/current-work/multipage/selectors.html#selector-in-range
 *   (in other words, one of the pseudo classes :in-range and :out-of-range
 *   should apply (which, depends on aValidity)).
 *   Else (neither :in-range or :out-of-range should match) set to false.
 */
function checkValidity(aElement, aValidity, aApply, aRangeApply)
{
  aValidity = aApply ? aValidity : true;

  is(aElement.validity.valid, aValidity,
     "element validity should be " + aValidity);
  is(aElement.validity.rangeUnderflow, !aValidity,
     "element underflow status should be " + !aValidity);
  var underflowMsg =
        (aElement.type == "date" || aElement.type == "time" ||
         aElement.type == "month" || aElement.type == "week" ||
         aElement.type == "datetime-local") ?
        ("Please select a value that is no earlier than " + aElement.min + ".") :
        ("Please select a value that is no less than " + aElement.min + ".");
  is(aElement.validationMessage,
     aValidity ? "" : underflowMsg, "Checking range underflow validation message");

  is(aElement.matches(":valid"), aElement.willValidate && aValidity,
     (aElement.willValidate && aValidity) ? ":valid should apply" : "valid shouldn't apply");
  is(aElement.matches(":invalid"), aElement.willValidate && !aValidity,
     (aElement.wil && aValidity) ? ":invalid shouldn't apply" : "valid should apply");

  if (!aRangeApply) {
    ok(!aElement.matches(":in-range"), ":in-range should not match");
    ok(!aElement.matches(":out-of-range"),
       ":out-of-range should not match");
  } else {
    is(aElement.matches(":in-range"), aValidity,
       ":in-range matches status should be " + aValidity);
    is(aElement.matches(":out-of-range"), !aValidity,
       ":out-of-range matches status should be " + !aValidity);
  }
}

for (var test of data) {
  input.type = test.type;
  var apply = test.apply;

  if (test.todo) {
    todo_is(input.type, test.type, test.type + " isn't implemented yet");
    continue;
  }

  // The element should be valid. Range should not apply when @min and @max are
  // undefined, except if the input type is 'range' (since that type has a
  // default minimum and maximum).
  if (input.type == 'range') {
    checkValidity(input, true, apply, true);
  } else {
    checkValidity(input, true, apply, false);
  }

  switch (input.type) {
    case 'hidden':
    case 'text':
    case 'search':
    case 'password':
    case 'url':
    case 'tel':
    case 'email':
    case 'number':
    case 'checkbox':
    case 'radio':
    case 'file':
    case 'submit':
    case 'reset':
    case 'button':
    case 'image':
    case 'color':
      input.min = '999';
      break;
    case 'date':
      input.min = '2012-06-27';
      break;
    case 'time':
      input.min = '20:20';
      break;
    case 'range':
      // range is special, since setting min to 999 will make it invalid since
      // it's default maximum is 100, its value would be 999, and it would
      // suffer from overflow.
      break;
    case 'month':
      input.min = '2016-06';
      break;
    case 'week':
      input.min = '2016-W39';
      break;
    case 'datetime-local':
      input.min = '2017-01-01T00:00';
      break;
    default:
      ok(false, 'please, add a case for this new type (' + input.type + ')');
  }

  // The element should still be valid and range should apply if it can.
  checkValidity(input, true, apply, apply);

  switch (input.type) {
    case 'text':
    case 'hidden':
    case 'search':
    case 'password':
    case 'tel':
    case 'radio':
    case 'checkbox':
    case 'reset':
    case 'button':
    case 'submit':
    case 'image':
    case 'color':
      input.value = '0';
      checkValidity(input, true, apply, apply);
      break;
    case 'url':
      input.value = 'http://mozilla.org';
      checkValidity(input, true, apply, apply);
      break;
    case 'email':
      input.value = 'foo@bar.com';
      checkValidity(input, true, apply, apply);
      break;
    case 'file':
      var file = new File([''], '635499_file');

      SpecialPowers.wrap(input).mozSetFileArray([file]);
      checkValidity(input, true, apply, apply);

      break;
    case 'date':
      input.value = '2012-06-28';
      checkValidity(input, true, apply, apply);

      input.value = '2012-06-27';
      checkValidity(input, true, apply, apply);

      input.value = 'foo';
      checkValidity(input, true, apply, apply);

      input.value = '2012-06-26';
      checkValidity(input, false, apply, apply);

      input.min = '2012-02-29';
      checkValidity(input, true, apply, apply);

      input.value = '2012-02-28';
      checkValidity(input, false, apply, apply);

      input.value = '1000-01-01';
      checkValidity(input, false, apply, apply);

      input.value = '20120-01-01';
      checkValidity(input, true, apply, apply);

      input.min = '0050-01-01';
      checkValidity(input, true, apply, apply);

      input.value = '0049-01-01';
      checkValidity(input, false, apply, apply);

      input.min = '';
      checkValidity(input, true, apply, false);

      input.min = 'foo';
      checkValidity(input, true, apply, false);
      break;
    case 'number':
      input.min = '0';
      input.value = '1';
      checkValidity(input, true, apply, apply);

      input.value = '0';
      checkValidity(input, true, apply, apply);

      input.value = 'foo';
      checkValidity(input, true, apply, apply);

      input.value = '-1';
      checkValidity(input, false, apply, apply);

      input.min = '-1';
      checkValidity(input, true, apply, apply);

      input.value = '-42';
      checkValidity(input, false, apply, apply);

      input.min = '';
      checkValidity(input, true, apply, false);

      input.min = 'foo';
      checkValidity(input, true, apply, false);

      // Check that we correctly convert input.min to a double in
      // validationMessage.
      input.min = "4.333333333333333333333333333333333331";
      input.value = "2";
      is(input.validationMessage,
         "Please select a value that is no less than 4.33333333333333.",
         "validation message");
      break;
    case 'range':
      input.min = '0';
      input.value = '1';
      checkValidity(input, true, apply, apply);

      input.value = '0';
      checkValidity(input, true, apply, apply);

      input.value = 'foo';
      checkValidity(input, true, apply, apply);

      input.value = '-1';
      checkValidity(input, true, apply, apply);

      is(input.value, input.min, "the value should have been set to min");

      input.min = '-1';
      checkValidity(input, true, apply, apply);

      input.value = '-42';
      checkValidity(input, true, apply, apply);

      is(input.value, input.min, "the value should have been set to min");

      input.min = '';
      checkValidity(input, true, apply, true);

      input.min = 'foo';
      checkValidity(input, true, apply, true);

      // We don't check the conversion of input.min to a double in
      // validationMessage for 'range' since range will always clamp the value
      // up to at least the minimum (so we will never see the min in a
      // validationMessage).

      break;
    case 'time':
      // Don't worry about that.
      input.step = 'any';

      input.min = '20:20';
      input.value = '20:20:01';
      checkValidity(input, true, apply, apply);

      input.value = '20:20:00';
      checkValidity(input, true, apply, apply);

      input.value = 'foo';
      checkValidity(input, true, apply, apply);

      input.value = '10:00';
      checkValidity(input, false, apply, apply);

      input.min = '20:20:00.001';
      input.value = '20:20';
      checkValidity(input, false, apply, apply);

      input.value = '00:00';
      checkValidity(input, false, apply, apply);

      input.value = '23:59';
      checkValidity(input, true, apply, apply);

      input.value = '20:20:01';
      checkValidity(input, true, apply, apply);

      input.value = '20:20:00.01';
      checkValidity(input, true, apply, apply);

      input.value = '20:20:00.1';
      checkValidity(input, true, apply, apply);

      input.min = '00:00:00';
      input.value = '01:00';
      checkValidity(input, true, apply, apply);

      input.value = '00:00:00.000';
      checkValidity(input, true, apply, apply);

      input.min = '';
      checkValidity(input, true, apply, false);

      input.min = 'foo';
      checkValidity(input, true, apply, false);
      break;
    case 'month':
      input.value = '2016-07';
      checkValidity(input, true, apply, apply);

      input.value = '2016-06';
      checkValidity(input, true, apply, apply);

      input.value = 'foo';
      checkValidity(input, true, apply, apply);

      input.value = '2016-05';
      checkValidity(input, false, apply, apply);

      input.min = '2016-01';
      checkValidity(input, true, apply, apply);

      input.value = '2015-12';
      checkValidity(input, false, apply, apply);

      input.value = '1000-01';
      checkValidity(input, false, apply, apply);

      input.value = '10000-01';
      checkValidity(input, true, apply, apply);

      input.min = '0010-01';
      checkValidity(input, true, apply, apply);

      input.value = '0001-01';
      checkValidity(input, false, apply, apply);

      input.min = '';
      checkValidity(input, true, apply, false);

      input.min = 'foo';
      checkValidity(input, true, apply, false);
      break;
    case 'week':
      input.value = '2016-W40';
      checkValidity(input, true, apply, apply);

      input.value = '2016-W39';
      checkValidity(input, true, apply, apply);

      input.value = 'foo';
      checkValidity(input, true, apply, apply);

      input.value = '2016-W38';
      checkValidity(input, false, apply, apply);

      input.min = '2016-W01';
      checkValidity(input, true, apply, apply);

      input.value = '2015-W53';
      checkValidity(input, false, apply, apply);

      input.value = '1000-W01';
      checkValidity(input, false, apply, apply);

      input.value = '10000-01';
      checkValidity(input, true, apply, apply);

      input.min = '0010-W01';
      checkValidity(input, true, apply, apply);

      input.value = '0001-W01';
      checkValidity(input, false, apply, apply);

      input.min = '';
      checkValidity(input, true, apply, false);

      input.min = 'foo';
      checkValidity(input, true, apply, false);
      break;
    case 'datetime-local':
      input.value = '2017-12-31T23:59';
      checkValidity(input, true, apply, apply);

      input.value = '2017-01-01T00:00';
      checkValidity(input, true, apply, apply);

      input.value = '2017-01-01T00:00:00.123';
      checkValidity(input, true, apply, apply);

      input.value = 'foo';
      checkValidity(input, true, apply, apply);

      input.value = '2016-12-31T23:59';
      checkValidity(input, false, apply, apply);

      input.min = '2016-01-01T00:00';
      checkValidity(input, true, apply, apply);

      input.value = '2015-12-31T23:59';
      checkValidity(input, false, apply, apply);

      input.value = '1000-01-01T00:00';
      checkValidity(input, false, apply, apply);

      input.value = '10000-01-01T00:00';
      checkValidity(input, true, apply, apply);

      input.min = '0010-01-01T12:00';
      checkValidity(input, true, apply, apply);

      input.value = '0010-01-01T10:00';
      checkValidity(input, false, apply, apply);

      input.min = '';
      checkValidity(input, true, apply, false);

      input.min = 'foo';
      checkValidity(input, true, apply, false);
      break;
    default:
      ok(false, 'write tests for ' + input.type);
  }

  // Cleaning up,
  input.removeAttribute('min');
  input.value = '';
}

</script>
</pre>
</body>
</html>
